import java.util.*;



public class BackPropANN
{
	//private final int BIAS = 1;
	public final int INPUTS = 2;  // a
	public final int HIDDEN = 2;  // b
	public final int OUTPUT = 1;  // c
	private final double ETA = 0.01;
	private final double ALPHA = 0.0;
	
	
	public double input2HiddenWts[][] = new double[INPUTS +1][HIDDEN];
	public double hidden2OutputWts[][] = new double[HIDDEN +1][OUTPUT];
		
	private double inputs[] = new double[INPUTS +1];
	private double hidden[] = new double[HIDDEN +1];
	private double output[] = new double[OUTPUT];
	private double target[] = new double[OUTPUT];
	private double deltaHidden[] = new double[HIDDEN +1];
	private double deltaOutput[] = new double[OUTPUT];
	private double deltaInput2HiddenWts[][] = new double[INPUTS +1][HIDDEN];
	private double deltaHidden2OutputWts[][] = new double[HIDDEN +1][OUTPUT];
	
	private static Random rnd = new Random();
		
	public double sigmoid(double x)
	{
		return (1.0 / (1.0 + Math.exp(-x)));
	}
	
	public void initWeights()
	{
		for(int i = 0; i < INPUTS + 1; i++){
			for(int j = 0; j < HIDDEN; j++){
				input2HiddenWts[i][j] = rnd.nextDouble()*2-1;
			}
		}
		for(int i = 0; i < HIDDEN + 1; i++){
			for(int j = 0; j < OUTPUT; j++){
				hidden2OutputWts[i][j] = rnd.nextDouble()*2-1;
			}
		}
	}
	
	public double[] feedForward(double[] inputs){
		forward2Hidden(inputs);
		forward2Output();
		return output;
	}
	public void forward2Hidden(double[] inputs)
	{
		this.inputs = inputs;
		this.inputs[INPUTS] = 1.0;
		for(int i = 0; i < HIDDEN; i++){
			double sum = 0.0;
			for(int j = 0; j < INPUTS + 1; j++){
				sum += inputs[j] * input2HiddenWts[j][i];
			}
			hidden[i] = sigmoid(sum);
		}
	}
	
	public void forward2Output()
	{
		hidden[HIDDEN] = 1.0;
		for(int i = 0; i < OUTPUT; i++){
			double sum = 0.0;
			for(int j = 0; j < HIDDEN + 1; j++){
				sum += hidden[j] * hidden2OutputWts[j][i];
			}
			output[i] = sigmoid(sum);
		}
	}
	
	public void deltasOnOutput(double[] target1)
	{
		target = target1;
		for(int k = 0; k < OUTPUT; k++){
			deltaOutput[k] = output[k] * (1.0 - output[k]) * (target[k] - output[k]);
		}
	}
	
	
	
	public void deltasOnHidden()
	{
		for(int j = 0; j < HIDDEN + 1; j++){
			double error = 0.0;
			for(int k = 0; k < OUTPUT; k++){
				error += hidden2OutputWts[j][k] * deltaOutput[k];
			}	
			deltaHidden[j] = hidden[j] * (1.0 - hidden[j]) * error;
		}
	}
	
	public void weightChanges()
	{
		for(int i = 0; i < OUTPUT; i++){
			for(int j = 0; j < HIDDEN + 1; j++){
				deltaHidden2OutputWts[j][i] = ETA * deltaOutput[i] * hidden[j] +
					ALPHA * deltaHidden2OutputWts[j][i];
				hidden2OutputWts[j][i] += deltaHidden2OutputWts[j][i];
			}
		}
		for(int i = 0; i < HIDDEN; i++){
			for(int j = 0; j < INPUTS + 1; j++){
				deltaInput2HiddenWts[j][i] = ETA * deltaHidden[i] * inputs[j] +
					ALPHA * deltaInput2HiddenWts[j][i];
				input2HiddenWts[j][i] += deltaInput2HiddenWts[j][i];
			}
		}
	}
	
	public double printSumOfSqrError()
	{
		double sumOfSqrErr = 0.0;
		for(int i = 0; i < output.length; i++){
			sumOfSqrErr += Math.pow(target[i] - output[i],2);
		}
		return sumOfSqrErr;
	}
	
	
	public void run()
	{
		double[][] inputs = {{0,0,1},
				  			 {0,1,1},
				  			 {1,0,1},
				  			 {1,1,1}};
		
		double[][] targets = {{0},{1},{1},{0}};
		
		initWeights();
		for(int i = 0; i <= 150000; i++){
			double e = 0.0;
			for(int j = 0; j < inputs.length; j++){
					forward2Hidden(inputs[j]);
					forward2Output();
					e += printSumOfSqrError();
					deltasOnOutput(targets[j]);
					deltasOnHidden();
					weightChanges();
			}
			System.out.println(e);
		}
		
	}
	
	
	public static void main(String[] args) 
	{
		BackPropANN ann = new BackPropANN();
		ann.run();
	}
}
